`include "defines.svh"
`include "CP0Defines.svh"

module Exception (
    input logic clk,
    input logic rst,

    //用于判断MEM级是否是延迟槽指令
    input logic MEM2_IsBranch,                        //MEM2级的指令是否是Branch
    input logic MEM2_Immjump,                         //MEM2级的指令是否是J型指令
    input ExceptionType MEM_ExceptionTypeNew,          //在前四级判断出的异常

    //CP0模块传进来的CP0寄存器的值
    input logic[31:0] CP0Status_in,
    input logic[31:0] CP0EPC_in,
    input logic[31:0] CP0Cause_in,   

    //CP0写、读信号,与CP0模块中是相同的
    input logic CP0Wr,  
    input logic[4:0] CP0WrAddr,
    input logic[31:0] CP0WrData,                

    output logic [2:0] EXE_Reg_Writed_Exception,       //如果存在异常，则EXE写寄存器信号为0
    //如果发生异常，则把前三级的流水线的寄存器清空
    output logic PREIF_IF_Flush_Exception,
    output logic IF_ID_Flush_Exception,
    output logic ID_EXE_Flush_Exception,
    output logic EXE_MEM_Flush_Exception,

    output logic [1:0] IsExceptionorEret,            //是否是异常或是Eret
    output ExceptionType MEM_ExceptionTypeEnd,
    output logic MEM_isDelaySlot,                   //当前MEM级的信号是否是延迟槽
    output logic[31:0] CP0EPC_out,
    output logic Is_Interrupt          
);

    //保存CP0中Status、Cause、EPC的最新值
    logic[31:0] CP0Status;
    logic[31:0] CP0Cause;
    logic[31:0] CP0EPC;
    assign MEM_isDelaySlot=MEM2_IsBranch||MEM2_Immjump;
    assign CP0EPC_out=CP0EPC;
    assign Is_Interrupt=MEM_ExceptionTypeEnd.Interrupt;
    //得到Status的最新值
    always_comb begin  
        if(!rst)begin
            CP0Status=32'b0;
        end
        else if(CP0Wr&&CP0WrAddr==`CP0StatusAddr)begin
            CP0Status = {16'd0, CP0WrData[15:8], 6'd0, CP0WrData[1:0]};
//            CP0Status[15:8]=CP0WrData[15:8];
//            CP0Status[1]=CP0WrData[1];
//            CP0Status[0]=CP0WrData[0];
        end
        else begin
            CP0Status=CP0Status_in;
        end
    end

    //得到CP0EPC的最新值
    always_comb begin
        if(!rst)begin
            CP0EPC=32'b0;
        end
        else if(CP0Wr&&CP0WrAddr==`CP0EPCAddr)begin
            CP0EPC=CP0WrData;
        end
        else begin
            CP0EPC=CP0EPC_in;
        end
    end

    //得到CP0Cause的最新值
    always_comb begin 
        if(!rst)begin
            CP0Cause=32'b0;
        end
        else if(CP0Wr&&CP0WrAddr==`CP0CauseAddr)begin
//            CP0Cause[9:8]=CP0WrData[9:8];
            CP0Cause = {22'd0, CP0WrData[9:8], 8'd0};
        end
        else begin
            CP0Cause=CP0Cause_in;
        end
    end

    always_comb begin
        if(MEM_ExceptionTypeEnd!=`NoException)begin
            EXE_Reg_Writed_Exception= 3'b000;            // 发生异常，关闭当前信号的写回寄存器使能信号
            PREIF_IF_Flush_Exception    = `FlushEnable;
            IF_ID_Flush_Exception=`FlushEnable;
            ID_EXE_Flush_Exception=`FlushEnable;
            EXE_MEM_Flush_Exception    = `FlushEnable;
            if(MEM_ExceptionTypeEnd.Eret==1'b1)begin
                IsExceptionorEret=`IsEret;
            end
            else begin
                IsExceptionorEret=`IsException;
            end      
        end
        else begin
            PREIF_IF_Flush_Exception    = `FlushDisable;
            IF_ID_Flush_Exception=`FlushDisable;
            ID_EXE_Flush_Exception=`FlushDisable;
            EXE_MEM_Flush_Exception    = `FlushDisable;
            IsExceptionorEret=`NoExceptionandEret;
            EXE_Reg_Writed_Exception=3'b111;
        end
    end

    assign MEM_ExceptionTypeEnd.Interrupt = (((CP0Cause[15:8] & CP0Status[15:8]) != 8'b0) && (CP0Status[1] == 1'b0) && (CP0Status[0] == 1'b1)) ?1'b1:1'b0;
    assign MEM_ExceptionTypeEnd.WrongAddrInIF       = MEM_ExceptionTypeNew.WrongAddrInIF;             
    assign MEM_ExceptionTypeEnd.ReservedInstr       = MEM_ExceptionTypeNew.ReservedInstr;
    assign MEM_ExceptionTypeEnd.Syscall             = MEM_ExceptionTypeNew.Syscall;
    assign MEM_ExceptionTypeEnd.Break               = MEM_ExceptionTypeNew.Break;
    assign MEM_ExceptionTypeEnd.Eret                = MEM_ExceptionTypeNew.Eret;
    assign MEM_ExceptionTypeEnd.WrongAddrInDataRd   = MEM_ExceptionTypeNew.WrongAddrInDataRd;
    assign MEM_ExceptionTypeEnd.WrongAddrInDataWr   = MEM_ExceptionTypeNew.WrongAddrInDataWr;
    assign MEM_ExceptionTypeEnd.Overflow            = MEM_ExceptionTypeNew.Overflow;
endmodule